# -*-coding = utf-8 -*-
# @Time :  22:29
# @Author : 邢志文
# @File : faceRecognition.py
# @Software : PyCharm
# 第一版本，只需要一张照片中有一张人脸

import cv2
from datetime import datetime
import zipfile
from werkzeug.utils import secure_filename
from flask import Flask, request, jsonify
import os
import random
import numpy as np
import shutil

# 分类器
classifier = cv2.CascadeClassifier(
    "D:/Tools/opencv/sources/data/haarcascades_cuda/haarcascade_frontalface_default.xml")
# # 输入照片存放路径
# filePathInput = "E:/Zjhy/Obj1_face/FaceRecognition_py/ImgInput/3.jpg"
# # 输出结构路径
# filePathOutput = "E:\\Zjhy\\Obj1_face\\FaceRecognition_py\\imgOutput"
class Faces():
    def __init__(self, leftUperX, leftUperY, rightDownX, rightDownY, centerLeftX, centerLeftY, centerRightX,
                 centerRightY, radiusMin, height, width):
        self.leftUperX = leftUperX  # 左上角X
        self.leftUperY = leftUperY  # 左上角Y
        self.rightDownX = rightDownX  # 右下角x
        self.rightDownY = rightDownY  # 右下角Y
        self.centerLeftX = centerLeftX  # 左眼x
        self.centerLeftY = centerLeftY  # 左眼 y
        self.centerRightX = centerRightX  # 右眼x
        self.centerRightY = centerRightY  # 右眼y
        self.radiusMin = radiusMin  # 人眼半径
        self.height = height  # 人脸照片高
        self.width = width  # 人脸照片宽

    # 该函数判断人眼是否在人脸之内，左眼比较左上角和左下角比较，右眼比较右上角和右下角比较
    def isCenterPointInRect(self):
        if not self.centerLeftX > self.leftUperX and self.centerLeftY > self.leftUperY:
            return False
        if not self.centerLeftX > self.leftUperX and self.centerLeftY < self.rightDowny:
            return False
        if not self.centerRightX < self.rightDownX and self.centerRightY > self.rightDowny:
            return False
        if not self.centerRightX < self.rightDownX and self.centerRightY < self.rightDowny:
            return False
        return True

# 指定程序主模块或者包的名字，__name__是系统变量，该变量指的是本py文件的文件名 filetype = 0:other; =1 img; =2 zip
app = Flask(__name__)
# 上传照片到api接口
@app.route("/upload", methods=["POST"])
def upload():
    f = request.files.get('file')
    # 获取安全的文件名 正常的文件名
    filename = secure_filename(f.filename)
    # 对文件类型进行判断，如果是单个照片，直接进行人脸检测并剪裁后返回，如果是压缩包，则解压后再操作
    fileType = fileDetermineType(filename)
    if fileType == 1:#如果发送的是图片，则对图片直接进行人脸检测剪裁
        # 生成随机数
        random_num = random.randint(0, 100)

        # f.filename.rsplit('.', 1)[1] 获取文件的后缀
        # 把文件重命名
        filename = datetime.now().strftime("%Y%m%d%H%M%S") + "_" + str(random_num) + "." + filename.rsplit('.', 1)[1]
        img = f.read()
        img1 = cv2.imdecode(np.frombuffer(img, np.uint8), cv2.IMREAD_COLOR)
        faceDetection(img1,filename)
        # zipFile = f.read()
        file_path = 'E:/Zjhy/Obj1_face/FaceRecognition_py/imgOutput/'+filename  # 上传的文件保存到的路径
        f.save(file_path)
        target_path = 'E:/Zjhy/Obj1_face/FaceRecognition_py/imgOutput/' # 解压后的文件保存到的路径
        unzip_file(file_path, target_path)
        os.remove(file_path)  # 删除文件
        fileList = os.listdir("E:/Zjhy/Obj1_face/FaceRecognition_py/imgOutput/")#遍历解压后的文件夹
        for item in fileList:
            itemPath = "E:/Zjhy/Obj1_face/FaceRecognition_py/imgOutput/"+item
            # with open(itemPath,"rb") as imgTransfer:
                # imgBinContents = imgTransfer.read()
                # imgBinTransfer = cv2.imdecode(np.frombuffer(imgBinContents, np.uint8), cv2.IMREAD_COLOR)
                # # 生成随机数
                # random_num = random.randint(0, 100)
                #
                # # f.filename.rsplit('.', 1)[1] 获取文件的后缀
                # # 把文件重命名
                # fileNameBinTransfer = datetime.now().strftime("%Y%m%d%H%M%S") + "_" + str(random_num) + "." + item.rsplit('.', 1)[1]
                # faceDetection(imgBinTransfer, fileNameBinTransfer)
            imgBinTransfer = cv2.imread(itemPath)
            random_num = random.randint(0, 100)
            fileNameBinTransfer = datetime.now().strftime("%Y%m%d%H%M%S") + "_" + str(random_num) + "." + \
                                  itemPath.rsplit('.', 1)[1]
            faceDetection(imgBinTransfer, fileNameBinTransfer)

        shutil.rmtree(target_path)
        os.mkdir(target_path)



    # 返回前端可调用的一个链接
    # 可以配置成对应的外网访问的链接
    my_host = "http://127.0.0.1:5000"
    new_path_file = my_host + "/imgOutput/" + filename
    data = {"msg": "success", "url": new_path_file}

    payload = jsonify(data)
    return payload, 200


# 此函数功能：读取照片，实现人脸照片的检测，分别检测出人脸和人眼
# 并判断人眼是否在人脸内，实现弱分类器到强分类器。
# 这样判断我检测的是人脸
#  -------h----->x
#  |
#  w
#  |
#  ↓
#  y
def faceDetection(imgOrigin,filename):
    print(filename)
    # imgOrigin = cv2.imread(filePath)  # 读取照片
    [imgOriginHeight, imgOriginWidth, imgOriginPixels] = imgOrigin.shape
    if imgOriginHeight < 200 or imgOriginWidth < 200:
        return False
    imgGray = cv2.cvtColor(imgOrigin, cv2.COLOR_BGR2GRAY)  # 转换成灰色图片
    # 调用识别人脸
    faceRects = classifier.detectMultiScale(imgGray, scaleFactor=1.2, minNeighbors=3, minSize=(32, 32))
    if len(faceRects):  # 大于0则检测到人脸
        for faceRect in faceRects:  # 单独框出每一张人脸
            x, y, w, h = faceRect
            leftUperX = x  # 左上点的坐标x
            leftUperY = y  # 左上点的坐标y
            height = h  # faceRect的h，我的理解是长
            width = w  # faceRect的w，我的理解是宽
            rightDownX = x + h  # 右下点坐标x
            rightDownY = y + w  # 右下点坐标y
            centerLeftX = x + w // 4  # 左眼圆心坐标x
            centerLeftY = y + h // 4 + 30  # 左眼圆心坐标y
            centerRightX = x + 3 * w // 4  # 右眼圆心坐标x
            centerRightY = y + h // 4 + 30  # 右眼圆心坐标y
            radiusMin = min(w // 8, h // 8)  # 半径
            # 实例化
            faces = Faces(leftUperX, leftUperY, rightDownX, rightDownY, centerLeftX, centerLeftY, centerRightX,
                          centerRightY, radiusMin, height, width)
            # 框出人脸
            cv2.rectangle(imgGray, (faces.leftUperX, faces.leftUperY), (faces.rightDownX, faces.rightDownY), (255, 0, 0),
                          2)
            # 左眼
            cv2.circle(imgGray, (faces.centerLeftX, faces.centerLeftY), faces.radiusMin, (255, 0, 0))
            # 右眼
            cv2.circle(imgGray, (faces.centerRightX, faces.centerRightY), faces.radiusMin, (255, 0, 0))
            # 强分类器
            if not faces.isCenterPointInRect():
                continue  # 此处为人眼不在人脸之内，我们判定不是人脸
            imgClippingOutput = faceClipping(imgOrigin, faces.leftUperX, faces.leftUperY, faces.rightDownX,
                                             faces.rightDownY, faces.width,
                                             faces.height, imgOriginWidth, imgOriginHeight)


    cv2.imwrite("E:/Zjhy/Obj1_face/FaceRecognition_py/result/"+filename, imgClippingOutput)
    print("9")




# 该函数实现人脸剪裁功能
# imgInput:输入图像，leftUperX, leftUperY, rightDownX, rightDownY分别是矩形的四角坐标
# widthBefore，heightBefore 识别到的人脸的照片的宽和高
def faceClipping(imgInput, leftUperXBefore, leftUperYBefore, rightDownXBefore, rightDownYBefore, widthBefore,
                 heightBefore, imgOriginWidth, imgOriginHeight):
    # 需要写保护，防止我剪裁的照片过大,这里我剪裁的大小用的widthAfter = widthBefore*1.5；heightAfter = widthAfter*(heightBefore/widthBefore)*1.2
    widthAfter = round(widthBefore * 1.5)
    heightAfter = round(widthAfter * (heightBefore / widthBefore) * 1.2)
    leftUperXOffeset = round((heightAfter - heightBefore) / 2)  # 左上角坐标的偏移量 也等于右下角的坐标的偏移量
    leftUperYOffeset = round((widthAfter - widthBefore) / 2)  # 左上角坐标的偏移量 也等于右下角的坐标的偏移量
    leftUperXAfter = round((leftUperXBefore - leftUperXOffeset) if (leftUperXBefore - leftUperXOffeset) > 0 else 4) # 防止剪裁的坐标<0
    leftUperYAfter = round((leftUperYBefore - leftUperYOffeset) if (leftUperYBefore - leftUperYOffeset) > 0 else 4)  # 防止剪裁的坐标<0
    rightDownXAfter = round((rightDownXBefore + leftUperXOffeset) if (rightDownXBefore + leftUperXOffeset) < imgOriginWidth else (imgOriginWidth - 4))
    rightDownYAfter = round((rightDownYBefore + leftUperYOffeset) if (rightDownYBefore + leftUperYOffeset) < imgOriginHeight else (imgOriginHeight - 4))
    imgOutput = imgInput[leftUperYAfter:rightDownYAfter,leftUperXAfter:rightDownXAfter]
    return imgOutput

#
# # 这个函数用来存放
# def imgRenameOutput(filePathName):
#     # 生成随机数
#     randomNum = random.randint(0, 100)
#     fileName = datetime.now().strftime("%Y%m%d%H%M%S") + "_" + str(randomNum) + "." + filename.rsplit('.', 1)[1]
#

# 输出剪裁后的人脸照片，并将其存放在指定文件夹,转存文件名用时间来命名
def faceImgOutput(imgInput):
    cv2.imshow(imgInput)

    # cv2.imwrite()


# 解压zip文件夹
# def unzipFile(zipFile, targetDir):
    # with zipfile.ZipFile(zipFile, "r") as zFile:
    #     for file in zFile.namelist():
    #         zFile.extract(file, targetDir)
def unzip_file(zip_src, dst_dir):
    """
    解压zip文件
    :param zip_src: zip文件的全路径
    :param dst_dir: 要解压到的目的文件夹
    :return:
    """
    r = zipfile.is_zipfile(zip_src)
    if r:
        fz = zipfile.ZipFile(zip_src, "r")
        for file in fz.namelist():
            fz.extract(file, dst_dir)
    else:
        return "请上传zip类型压缩文件"

# 判断文件类型
# other = 0  img = 1   zip = 2
def fileDetermineType(filePathName):
    imgTypeList = {'jpg', 'bmp', 'png', 'jpeg', 'rgb', 'tif'}
    fileExtension = filePathName.rsplit('.', 1)[1]
    fileExtension = fileExtension.lower()  # 转小写
    if fileExtension in imgTypeList:
        return 1
    elif fileExtension == "zip":
        return 2
    else:
        return 0


#遍历解压后的文件夹，并处理人脸剪裁
def foldTraverseProcessing(filepath):
    fileList = os.listdir("E:/Zjhy/Obj1_face/FaceRecognition_py/imgOutput/")  # 遍历解压后的文件夹
    for item in fileList:
        print(item)
        print(type(item))
        # if fileDetermineType(item) == 1:
            # faceDetection()




# 项目函数入口
if __name__ == "__main__":
    # faceDetection("E:/Zjhy/Obj1_face/FaceRecognition_py/ImgInput/3.jpg")
    app.run()